﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Entidades.jSON;
using NHibernate;
using NHibernate.Linq;
using NHibernate.Criterion;
using System.Linq.Expressions;
using System.Reflection;
using System.Linq.Dynamic;
using Entidades;
using Common;
using Persistence;

namespace Persistencia.Repositorios
{
    public class GuionRepositorio
    {
        private readonly ISession _session;

        #region Constructors

        /// <summary>
        /// Constructor por defecto
        /// </summary>
        public GuionRepositorio()
        {
            try
            {
                _session = NHibernateSessionHelper.GetSession();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        #endregion

        #region Métodos

        #region Guion

        /// <summary>
        /// Devuelve un Guion según su Id
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public Guion ObtenerGuion(int id)
        {
            Guion guion = new Guion();
            try
            {
                using (ITransaction transaction = _session.BeginTransaction())
                {
                    guion = _session.Query<Guion>().Where(x => x.Id == id).FirstOrDefault();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return guion;
        }

        /// <summary>
        /// Devuelve todos los Guiones, según un filtro y paginación
        /// </summary>
        /// <param name="paginacion"></param>
        /// <param name="filtro"></param>
        /// <returns></returns>
        public List<Guion> ObtenerGuiones(Data.Pagination paginacion, string filtro)
        {
            var lista = new List<Guion>();
            try
            {
                using (ITransaction transaction = _session.BeginTransaction())
                {
                    if (filtro.Length == 0)
                        filtro = "1 == 1";

                    if (paginacion == null)
                    {
                        #region Non-paged query
                        lista = _session.Query<Guion>()
                                .Where(filtro)
                                .OrderBy(x => x.Id)
                                .ToList();
                        #endregion
                    }
                    else
                    {
                        #region Paged query
                        lista = _session.Query<Guion>()
                                .Where(filtro)
                                .OrderBy(paginacion.SortColumn + " " + paginacion.SortOrder)
                                .Skip((paginacion.CurrentPage - 1) * paginacion.PageSize)
                                .Take(paginacion.PageSize)
                                .ToList();
                        #endregion
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return lista;
        }

        /// <summary>
        /// Devuelve el total de Guiones asociados a un Actor
        /// </summary>
        /// <param name="idActor"></param>
        /// <param name="filtro"></param>
        /// <returns></returns>
        public int ObtenerTotalGuionesDelActor(int idActor, string filtro)
        {
            int total = 0;
            try
            {
                using (ITransaction transaction = _session.BeginTransaction())
                {
                    if (filtro.Length == 0)
                        filtro = "1 == 1";

                    total = _session.Query<GuionItem>()
                        .Where(filtro)
                        .Where(x => x.Actor.Id == idActor)
                        .Count();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return total;
        }

        /// <summary>
        /// Devuelve todos los Guiones asociados a un Actor
        /// </summary>
        /// <param name="paginacion"></param>
        /// <param name="filtro"></param>
        /// <returns></returns>
        public List<GuionItem> ObtenerGuionesDelActor(int idActor, Data.Pagination paginacion, string filtro)
        {
            var lista = new List<GuionItem>();
            try
            {
                using (ITransaction transaction = _session.BeginTransaction())
                {
                    if (filtro.Length == 0)
                        filtro = "1 == 1";

                    if (paginacion == null)
                    {
                        #region Non-paged query
                        lista = _session.Query<GuionItem>()
                                .Where(filtro)
                                .Where(x => x.Actor.Id == idActor)
                                .OrderBy(x => x.Id)
                                .ToList();
                        #endregion
                    }
                    else
                    {
                        #region Paged query
                        lista = _session.Query<GuionItem>()
                                .Where(filtro)
                                .Where(x => x.Actor.Id == idActor)
                                .OrderBy(paginacion.SortColumn + " " + paginacion.SortOrder)
                                .Skip((paginacion.CurrentPage - 1) * paginacion.PageSize)
                                .Take(paginacion.PageSize)
                                .ToList();
                        #endregion
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return lista;
        }
        
        /// <summary>
        /// Devuelva una lista de Guiones que están Pendientes de Pago
        /// [Opcional] Id Tipo de Usuario
        /// [Opcional] Id Categoría de Usuario
        /// [Opcional] Id Usuario
        /// [Opcional] Id Tipo Usuario a ignorar
        /// </summary>
        /// <param name="idProyecto"></param>
        /// <param name="idTipoUsuario"></param>
        /// <param name="idCategoriaUsuario"></param>
        /// <param name="idUsuario"></param>
        /// <param name="idTipoUsuarioIgnorado"></param>
        /// <returns></returns>
        public IList<Guion> ObtenerGuionesPendientesDePago(int idProyecto, int? idTipoUsuario, int? idCategoriaUsuario = null, int? idUsuario = null, int? idTipoUsuarioIgnorado = null)
        {
            var guiones = new List<Guion>();
            try
            {
                using (var transaction = _session.BeginTransaction())
                {
                    var query = _session.Query<Pago>()
                       .Where
                        (
                            x =>
                            x.Guion.Proyecto.Id    == idProyecto &&
                            x.Guion.Estado.Id      == WellKnownKeys.Entities.EstadosGuion.FINALIZADO.Id &&
                            x.Estado.Id            == WellKnownKeys.Entities.EstadosPago.PENDIENTE_DE_PAGO.Id &&
                            x.Proveedor            != null
                        );

                    if ( idTipoUsuario.HasValue )
                        query = query.Where(x => x.ProveedorRol != null && x.ProveedorRol.Id == idTipoUsuario);

                    if (idTipoUsuarioIgnorado.HasValue)
                        query = query.Where(x => x.ProveedorRol != null && x.ProveedorRol.Id != idTipoUsuarioIgnorado.Value);

                    if ( idCategoriaUsuario.HasValue )
                        query = query.Where(x => x.Proveedor.Categoria != null && x.Proveedor.Categoria.Id == idCategoriaUsuario);

                    if ( idUsuario.HasValue )
                        query = query.Where(x => x.Proveedor.Id == idUsuario);

                    guiones = query
                                .DistinctBy(x => x.Guion.Id)
                                .OrderBy(x => x.Guion.Episodio)
                                .ThenBy(x => x.Guion.Titulo)
                                .Select(pago => new Guion()
                                {
                                    Id          = pago.Guion.Id,
                                    Titulo      = pago.Guion.Titulo,
                                    Episodio    = pago.Guion.Episodio
                                }).ToList();
                }
            }
            catch (Exception)
            {
                throw;
            }
            return guiones;
        }

        ///// <summary>
        ///// Obtiene los Guiones en los que participó un Usuario, como Adaptador/Director/Mezclador/Traductor/Actor
        ///// </summary>
        ///// <param name="idUsuario"></param>
        ///// <returns></returns>
        //public IList<Guion> ObtenerGuiones(int? idUsuario = null)
        //{
        //    var guiones = new List<Guion>();
        //    try
        //    {
        //        using (var transaction = _session.BeginTransaction())
        //        {
        //            var query = _session.Query<GuionItem>();

        //            if ( idUsuario.HasValue )
        //            {
        //                query = query
        //                   .Where
        //                    (
        //                        x =>
        //                        (x.Actor != null && x.Actor.Id == idUsuario) ||
        //                        (x.Guion.Adaptador != null && x.Guion.Adaptador.Id == idUsuario) ||
        //                        (x.Guion.Director != null && x.Guion.Director.Id == idUsuario) ||
        //                        (x.Guion.Mezclador != null && x.Guion.Mezclador.Id == idUsuario) ||
        //                        (x.Guion.Traductor != null && x.Guion.Traductor.Id == idUsuario)
        //                    );
        //            }

        //            guiones = query
        //                        .DistinctBy(x => x.Guion.Id)
        //                        .OrderBy(x => x.Guion.Titulo)
        //                        .ThenBy(x => x.Guion.Episodio)
        //                        .Select(guionItem => new Guion()
        //                        {
        //                            Id          = guionItem.Guion.Id,
        //                            Titulo      = guionItem.Guion.Titulo,
        //                            Episodio    = guionItem.Guion.Episodio
        //                        }).ToList();
        //        }
        //    }
        //    catch (Exception)
        //    {
        //        throw;
        //    }
        //    return guiones;
        //}

        /// <summary>
        /// Devuelve el total de Guiones, según un filtro
        /// </summary>
        /// <param name="filtro"></param>
        /// <returns></returns>
        public int ObtenerTotalGuiones(string filtro)
        {
            int total = 0;
            try
            {
                using (ITransaction transaction = _session.BeginTransaction())
                {
                    if (filtro.Length == 0)
                        filtro = "1 == 1";

                    total = _session.Query<Guion>()
                        .Where(filtro)
                        .Count();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return total;
        }

        /// <summary>
        /// Guarda un Guión
        /// </summary>
        /// <param name="guion"></param>
        /// <returns></returns>
        public bool GuardarGuion(Guion guion)
        {
            bool resultado = false;
            try
            {
                using (ITransaction transaction = _session.BeginTransaction())
                {
                    _session.SaveOrUpdate(guion);
                    _session.Flush();
                    transaction.Commit();
                    resultado = true;
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return resultado;    
        }

        #endregion

        #region Guion Item

        /// <summary>
        /// Devuelve un GuionItem según su Id
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public GuionItem ObtenerGuionItem(int id)
        {
            GuionItem guionItem = new GuionItem();
            try
            {
                using (ITransaction transaction = _session.BeginTransaction())
                {
                    guionItem = _session.Query<GuionItem>().Where(x => x.Id == id).FirstOrDefault();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return guionItem;
        }

        /// <summary>
        /// Guardar el Ítem de un Guión
        /// <param name="item"></param>
        /// </summary>
        /// <returns></returns>
        public bool GuardarItem(GuionItem item)
        {
            bool resultado = false;
            try
            {
                using (ITransaction transaction = _session.BeginTransaction())
                {
                    _session.SaveOrUpdate(item);
                    _session.Flush();
                    transaction.Commit();
                    resultado = true;
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return resultado;
        }

        /// <summary>
        /// Elimina un Ítem de un Guión
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public bool EliminarItem(GuionItem item)
        {
            bool resultado = false;
            try
            {
                using (ITransaction transaction = _session.BeginTransaction())
                {
                    _session.Delete(item);
                    _session.Flush();
                    transaction.Commit();
                    resultado = true;
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return resultado;
        }

        #endregion

        #region Guion Estado

        /// <summary>
        /// Devuelve un GuionEstado según su Id
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public GuionEstado ObtenerGuionEstado(int id)
        {
            GuionEstado guionEstado = new GuionEstado();
            try
            {
                using (ITransaction transaction = _session.BeginTransaction())
                {
                    guionEstado = _session.Query<GuionEstado>().Where(x => x.Id == id).FirstOrDefault();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return guionEstado;
        }

        /// <summary>
        /// Devuelve todos los GuionEstado
        /// </summary>
        /// <returns></returns>
        public List<GuionEstado> ObtenerEstadosGuion()
        {
            List<GuionEstado> lista = new List<GuionEstado>();
            try
            {
                using (ITransaction transaction = _session.BeginTransaction())
                {
                    lista = _session.Query<GuionEstado>()
                            .OrderBy(x => x.Nombre)
                            .ToList();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return lista;
        }

        #endregion

        #region Costo

        /// <summary>
        /// Devuelve el Costo de un Guion
        /// </summary>
        /// <param name="idGuion"></param>
        /// <returns></returns>
        public GuionCosto ObtenerGuionCosto(int idGuion)
        {
            var guionCosto = new GuionCosto();
            try
            {
                using (ITransaction transaction = _session.BeginTransaction())
                {
                    guionCosto = _session.Query<GuionCosto>().Where(x => x.Guion.Id == idGuion).FirstOrDefault();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return guionCosto;
        }

        /// <summary>
        /// Guarda un GuionCosto
        /// </summary>
        /// <param name="guionCosto"></param>
        /// <returns></returns>
        public bool GuardarGuionCosto(GuionCosto guionCosto)
        {
            bool resultado = false;
            try
            {
                using (ITransaction transaction = _session.BeginTransaction())
                {
                    _session.SaveOrUpdate(guionCosto);
                    _session.Flush();
                    transaction.Commit();
                    resultado = true;
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return resultado;    
        }

        #endregion

        #region Tipo Cancion

        /// <summary>
        /// Devuelve un TipoCancion según su Id
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public TipoCancion ObtenerTipoCancion(int id)
        {
            TipoCancion guionItem = new TipoCancion();
            try
            {
                using (ITransaction transaction = _session.BeginTransaction())
                {
                    guionItem = _session.Query<TipoCancion>().Where(x => x.Id == id).FirstOrDefault();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return guionItem;
        }

        /// <summary>
        /// Deuelve todos los TipoCancion
        /// </summary>
        /// <returns></returns>
        public List<TipoCancion> ObtenerTiposCancion()
        {
            List<TipoCancion> lista = new List<TipoCancion>();
            try
            {
                using (ITransaction transaction = _session.BeginTransaction())
                {
                    lista = _session.Query<TipoCancion>()
                            .OrderBy(x => x.Nombre)
                            .ToList();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return lista;
        }

        #endregion

        #endregion
    }
}
